package utility;

import java.io.*;
import java.util.*;

public class SequentialByteBufferInputStream extends InputStream {

   private class Buffer {
      public byte[] buffer;
      public int start;
      public int end;
   }
   
   private List<Buffer> buffers;
   private int length = 0;
   private int pos = 0;
   private int index = 0;
   
   public SequentialByteBufferInputStream(int capacity, byte[]... buffers) {
      this.buffers = new ArrayList<Buffer>(capacity);
      
      for (byte[] buffer : buffers) {
         this.add(buffer);
      }
   }
   
   public void add (byte[] buffer) {
      Buffer b = new Buffer();
      b.buffer = buffer;
      b.start = this.length;
      b.end = this.length + buffer.length;
      this.buffers.add(b);
      
      this.length += buffer.length;
   }
   
   public void add (byte[] buffer, int length) {
      Buffer b = new Buffer();
      b.buffer = buffer;
      b.start = this.length;
      b.end = this.length + length;
      this.buffers.add(b);
      
      this.length += length;
   }
   
   public int length () {
      return length;
   }

   @Override
   public int read() throws IOException {
      
      if (pos >= this.length) {
         return -1;
      }
      
      Buffer b = buffers.get(index);
      
      byte bt = b.buffer[pos - b.start];
      
      if (pos++ >= b.end) {
         index++;
      }
      
      return bt;
     
   }
   
   @Override
   public int read(byte[] buffer) throws IOException {
      return this.read(buffer, 0, buffer.length);
   }
   
   @Override
   public int read (byte[] buffer, int offset, int length) throws IOException {
      
      // Check how much we have left in the buffer
      int remaining = this.length - this.pos;
      
      // Check how much we have to read
      int read = (remaining < length ? remaining : length);
      
      // Keep track of the total we had to read, for the return value
      int total = read;
      
      // If we have nothing left in the buffer, return the end-of-stream code
      if (remaining <= 0)
         return -1;
      
      // Keep looping while we have stuff to read
      while (read > 0) {
         
         // Get our current buffer
         Buffer b = buffers.get(index);
         
         // Determine how much we have in this buffer
         int bufferLength = b.end - b.start;
         
         // Determine how much we're going to read in this iteration
         int currentRead = (read < bufferLength ? read : bufferLength);
         
         // Copy the source buffer to the destination buffer
         System.arraycopy(b.buffer, this.pos - b.start, buffer, offset, currentRead);
         
         // Advance the counters
         pos += currentRead;
         offset += currentRead;
         read -= currentRead;
         
         // Move to the next buffer if necessary
         if (pos >= b.end) {
            index++;
         }
      }
      
      // Return the total that we read
      return total;
   }
   
}
